package com.example.excelapp.utils;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.excelapp.entity.UserData;
import com.example.excelapp.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ExcelUtil {

    /**
     * 具体流程如下：
     * 1、读取 Excel 文件：使用 EasyExcel 逐行读取 Excel 文件中的数据。（此步在 UserServiceImpl#importExcel 方法中调用）
     * 2、累积到一定数量后保存：每当累积到 10,000 条数据时，调用 saveBatch 方法将数据批量保存到数据库中。
     * 3、清空缓存，继续累积：保存完成后，清空列表，继续累积下一批数据。
     * 4、日志记录：每一批保存完成后通过日志记录导入的条数、批次和耗时。
     * 5、处理最后一批数据：当所有数据读取完成后，如果剩余数据不足 10,000 条，也会进行一次保存。
     * <p>
     * 优化记录：
     * 1、使用 Spring 的事务管理器，实现对批量插入操作的事务控制。=》效果明显
     */
    public static class UserDataListener extends AnalysisEventListener<UserData> {

        private static final int BATCH_COUNT = 100000;
        private final UserService userService;
        private final PlatformTransactionManager transactionManager;  // 用于事务管理
        private List<UserData> list = new ArrayList<>();
        private int batchCount = 0;  // 用于记录批次数
        private long startTime = System.currentTimeMillis();  // 记录开始时间

        public UserDataListener(UserService userService, PlatformTransactionManager transactionManager) {
            this.userService = userService;
            this.transactionManager = transactionManager;
        }

        @Override
        public void invoke(UserData data, AnalysisContext context) {

            list.add(data);
            if (list.size() >= BATCH_COUNT) {
                saveData();
                list.clear();
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            saveData();
            log.info("所有数据分析完成，导入总耗时: {} ms", System.currentTimeMillis() - startTime);
        }

        /**
         * 保存数据到数据库，并记录日志，同时每批次使用独立事务。
         */
        private void saveData() {
            if (!list.isEmpty()) {
                // 创建事务定义
                DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

                // 开启事务
                TransactionStatus status = transactionManager.getTransaction(def);

                try {
                    // 批量插入数据
                    userService.saveBatch(list);
                    transactionManager.commit(status);  // 提交事务
                    batchCount++;
                    log.info("批次 {}: 导入 {} 条数据，累计耗时: {} ms", batchCount, list.size(), System.currentTimeMillis() - startTime);
                } catch (Exception e) {
                    // 如果插入失败，回滚事务
                    transactionManager.rollback(status);
                    log.error("批次 {}: 导入失败，已回滚事务", batchCount, e);
                }
            }
        }
    }
}

